#include "m_Bmp.h"


m_Bmp::m_Bmp(void):bfHeader(nullptr), biHeader(nullptr), BitmapData(nullptr), RGBData(nullptr)
{
}

m_Bmp::~m_Bmp(void)
{
    delete[] BitmapData;
}

bool m_Bmp::Load(char* fileName)
{
    //文件载入，失败返回
    if (fileName == nullptr)
    {
        std::cerr << "empty filename" << std::endl;
        return false;
    }

    std::ifstream file(fileName, std::ios::in|std::ios::binary);
    if (file.fail())
    {
        std::cerr << "no file" << std::endl;
        return false;
    }
	WORD byte;
	file.read((char*)&byte,2);
	if (byte != BFT_BITMAP)
	{
		std::cerr << "file isn't bitmap" << std::endl;
		return false;
	}

    //文件数据读取
    file.seekg(0, std::ios::end);
    nLen = (int)file.tellg();

    try {
        BitmapData = new BYTE[nLen]();
    } catch (const std::bad_alloc&) {
        std::cerr << "not enough memory" << std::endl;
        return false;
    }
    file.seekg(0, std::ios::beg);
    file.read((char *)BitmapData, nLen);
	file.close();

	//成员赋值
	bfHeader = (BITMAPFILEHEADER*)BitmapData;
    biHeader = (BITMAPINFOHEADER*)(BitmapData + sizeof(BITMAPFILEHEADER));
    nWidth = biHeader->biWidth;
    nSizePerLine = (nWidth * 3 + 3) / 4 * 4;
    nHeight = biHeader->biHeight;
    RGBData = BitmapData + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    //色深为8时，读调色板并重构位图
    if (biHeader->biBitCount < 24)
    {
        //调色板长度
        UINT uPalette = biHeader->biClrUsed;//获取颜色数
        if (uPalette == 0)
            uPalette = 1 << biHeader->biBitCount;
        RGBQUAD *pPalette = (RGBQUAD *)RGBData;//指向调色板的指针
        RGBData += sizeof(RGBQUAD) * uPalette;//调整图像数据指针位置

        //根据调色板重构位图
        BYTE* pTemp = BitmapData;
        nLen = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + nSizePerLine * nHeight;
        try{
            BitmapData = new BYTE[nLen]();
        } catch (const std::bad_alloc&) {
            std::cerr << "not enough memory" << std::endl;
            return false;
        }

        //成员指针更新
        bfHeader = (BITMAPFILEHEADER *)BitmapData;
        biHeader = (BITMAPINFOHEADER *)(BitmapData + sizeof(BITMAPFILEHEADER));

        BYTE* pRGBDataTemp = RGBData;
        RGBData = BitmapData + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

        //位图数据更新
        bfHeader->bfType = 'B' + 'M' * 256;
        bfHeader->bfSize = nLen;
        bfHeader->bfReserved1 = 0;
        bfHeader->bfReserved2 = 0;
        bfHeader->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
        biHeader->biSize = sizeof(BITMAPINFOHEADER);
        biHeader->biWidth = nWidth;
        biHeader->biHeight = nHeight;
        biHeader->biPlanes = 1;
        biHeader->biBitCount = 24;
        biHeader->biCompression = BI_RGB;
        biHeader->biSizeImage = 0;
        biHeader->biXPelsPerMeter = 0;
        biHeader->biYPelsPerMeter = 0;
        biHeader->biClrUsed = 0;
        biHeader->biClrImportant = 0;

		//点坐标x，y，点位置，点数据位，调色板的位置
        int x, y, point, pointByte, Palette;

        //根据调色板对像素点数据进行调整
        for (y = 0; y < nHeight; y++)
        {
            for (x = 0; x < nWidth; x++)
            {
                point = y * ((nWidth + 3) / 4 * 4) + x;
				pointByte = y * nSizePerLine + x * 3;
                if (pRGBDataTemp[point] < uPalette)
                    Palette = pRGBDataTemp[point];
                else
                    Palette = 0;
                RGBData[pointByte] = pPalette[Palette].rgbBlue;
                RGBData[pointByte + 1] = pPalette[Palette].rgbGreen;
                RGBData[pointByte + 2] = pPalette[Palette].rgbRed;
            }
        }

        delete[] pTemp;//析构指针，需确保没有指针指向该段内存
    }

    return true;
}

bool m_Bmp::SaveAs(char *fileName = "unknow.bmp")
{
	if (BitmapData == nullptr) {
		std::cerr << "no bitmap data" << std::endl;
		return false;
	}

    std::ofstream file(fileName,std::ios::out|std::ios::binary);
    if (file.fail()) {
        std::cerr << "file writing fail" << std::endl;
        return false;
    }
    file.write((char*)BitmapData, nLen);
    file.close();
    return true;
}
